#ifndef _INTERFACE_CONSOLE_EXPRESSION_MENU_
#define _INTERFACE_CONSOLE_EXPRESSION_MENU_

#include "expression_fraction_menu.h"
#include "..\..\mathematics\algebra\expression.h"
#include "..\..\mathematics\algebra\expression_fraction.h"
using namespace science::mathematics::algebra;

#include <memory>
#include <iostream>
#include <string>
using std::cout;
using std::cin;
using std::endl;

#include "console_settings.h"

namespace science
{
	namespace interface
	{
		namespace console
		{
			enum class EXPRESSION_MENU_OPTIONS
			{
				OPTION_EXPRESSION_EQUALITY,
				OPTION_EXPRESSION_ADDITION,
				OPTION_EXPRESSION_SUBTRACTION,
				OPTION_EXPRESSION_MULTIPLICATION,
				OPTION_EXPRESSION_DIVISION,
				OPTION_EXPRESSION_SIMPLIFICATION,
				OPTION_EXPRESSION_EVALUATE,
				OPTION_EXPRESSION_SAVING,
				OPTION_EXPRESSION_CLEAR_MEMORY,
				OPTION_EXPRESSION_EXIT,
				OPTION_EXPRESSION_UNKNOWN
			};

			class expression_menu
			{
			private:

				static std::shared_ptr<expression> _saved_expression;

			public:
				static EXPRESSION_MENU_OPTIONS show_menu()
				{
					console_settings::clear_screen();

					cout << "Choose option from the next menu:" << endl;
					cout << "  1  - expression = expression?." << endl;
					cout << "  2  - expression + expression." << endl;
					cout << "  3  - expression - expression." << endl;
					cout << "  4  - expression * expression." << endl;
					cout << "  5  - expression / expression." << endl;
					cout << "  6  - simplify expression." << endl;
					cout << "  7  - evaluate expression." << endl;
					cout << "  8  - save expression in memory." << endl;
					cout << "  9  - clear memory." << endl;
					cout << "  10 - Exit program." << endl << endl;

					int option = console_settings::get_int_option("Your option? ");

					switch (option)
					{
					case 1:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_EQUALITY;
					case 2:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_ADDITION;
					case 3:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_SUBTRACTION;
					case 4:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_MULTIPLICATION;
					case 5:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_DIVISION;
					case 6:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_SIMPLIFICATION;
					case 7:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_EVALUATE;
					case 8:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_SAVING;
					case 9:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_CLEAR_MEMORY;
					case 10:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_EXIT;
					default:
						return EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_UNKNOWN;
					}
				}

				static void run()
				{
					while (true)
					{
						EXPRESSION_MENU_OPTIONS option = expression_menu::show_menu();
						switch (option)
						{
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_EQUALITY:
							check_expression_equality();
							break;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_ADDITION:
							add_two_expressions();
							break;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_SUBTRACTION:
							subtract_two_expressions();
							break;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_MULTIPLICATION:
							multiply_two_expressions();
							break;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_DIVISION:
							divide_two_expressions();
							break;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_SIMPLIFICATION:
							simplify_expression();
							break;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_EVALUATE:
							evaluate_expression();
							break;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_SAVING:
							save_expression_in_memory();
							break;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_CLEAR_MEMORY:
							_saved_expression = nullptr;
							break;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_EXIT:
							return;
						case science::interface::console::EXPRESSION_MENU_OPTIONS::OPTION_EXPRESSION_UNKNOWN:
							cout << endl << "Invalid option, please choose again.." << endl;
							break;
						default:
							break;
						}
					};
				}

			public:

				static bool load_from_memory()
				{
					if (_saved_expression == nullptr)
						return false;

					char option = console_settings::get_char_option("Do you want so load the saved expression? ");
					if (option == 'Y' || option == 'y')
					{
						cout << "The loaded expression is : " << *(_saved_expression.get()) << endl;
						return true;
					}
					else
						return false;
				}

				static void load_two_expressions(expression& frc1, expression& frc2)
				{
					if (load_from_memory())
						frc1 = *(_saved_expression.get());
					else
						frc1 = console_settings::get_from_user<expression>("Enter first expression : ");

					frc2 = console_settings::get_from_user<expression>("Enter second expression : ");
				}

				static expression load_one_expression()
				{
					if (load_from_memory())
						return *(_saved_expression.get());
					else
						return console_settings::get_from_user<expression>("Enter expression : ");
				}

				static void check_expression_equality()
				{
					expression frc1, frc2;

					console_settings::clear_screen();
					load_two_expressions(frc1, frc2);

					if (frc1 == frc2)
						cout << "The two expressions : " << frc1 << " and " << frc2 << " are equals." << endl;
					else
						cout << "The two expressions : " << frc1 << " and " << frc2 << " are not equals." << endl;

					if (console_settings::do_again())
						check_expression_equality();
				}

				static void add_two_expressions()
				{
					expression frc1, frc2;

					console_settings::clear_screen();
					load_two_expressions(frc1, frc2);

					expression result = frc1 + frc2;
					cout << frc1 << " + " << frc2 << " = " << result << endl;
					save_result_in_memory(result);

					if (console_settings::do_again())
						add_two_expressions();
				}

				static void subtract_two_expressions()
				{
					expression frc1, frc2;

					console_settings::clear_screen();
					load_two_expressions(frc1, frc2);

					expression result = frc1 - frc2;
					cout << frc1 << " - " << frc2 << " = " << result << endl;
					save_result_in_memory(result);

					if (console_settings::do_again())
						subtract_two_expressions();
				}

				static void multiply_two_expressions()
				{
					expression frc1, frc2;

					console_settings::clear_screen();
					load_two_expressions(frc1, frc2);

					expression result = frc1 * frc2;
					cout << frc1 << " * " << frc2 << " = " << result << endl;
					save_result_in_memory(result);

					if (console_settings::do_again())
						multiply_two_expressions();
				}

				static void divide_two_expressions()
				{
					expression frc1, frc2;

					console_settings::clear_screen();
					load_two_expressions(frc1, frc2);

					expression_fraction result = frc1 / frc2;
					cout << frc1 << " / " << frc2 << " = " << result << endl;
					expression_fraction_menu::save_result_in_memory(result);

					if (console_settings::do_again())
						divide_two_expressions();
				}

				static void simplify_expression()
				{
					console_settings::clear_screen();
					expression frc = load_one_expression();

					expression result = frc.simplify();
					cout << "Simplifying : " << frc << " becomes " << result << endl;
					save_result_in_memory(result);

					if (console_settings::do_again())
						simplify_expression();
				}

				static void evaluate_expression()
				{
					console_settings::clear_screen();
					expression frc = load_one_expression();
					cout << "Enter the values of variables (variable power): ";
					std::map<char, double> vals;
					for (unsigned int i = 0; i < frc.get_max_power_term().get_variables_count(); i++)
					{
						double val; char var_name;
						cin >> var_name >> val;
						vals[var_name] = val;
					}
					cout << frc << " evluates to : " << frc.evaluate(vals) << endl;
					if (console_settings::do_again())
						evaluate_expression();
				}

				static void save_result_in_memory(expression frc)
				{
					if (console_settings::do_again("Save the result to memory? "))
					{
						_saved_expression = std::shared_ptr<expression>(new expression());
						*_saved_expression = frc;
					}
				}

				static void save_expression_in_memory()
				{
					console_settings::clear_screen();
					expression frc = console_settings::get_from_user<expression>("Enter expression : ");
					_saved_expression = std::shared_ptr<expression>(new expression());
					*_saved_expression = frc;
					cout << "Expression saved." << endl;
				}
			};

			std::shared_ptr<expression> expression_menu::_saved_expression = nullptr;
		}
	}
}

#endif